// 使用结构体组织相关联的数据
// struct，或者 structure，是一个自定义数据类型，允许你命名和包装多个相关的值，
// 从而形成一个有意义的组合。如果你熟悉一门面向对象语言，struct 就像对象中的数据属性。


// 定义结构体
// 在大括号中，定义每一部分数据的名字和类型，我们称为 字段（field）
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn structure_1() {
    // 注意整个实例必须是可变的；Rust 并不允许只将某个字段标记为可变
    let mut user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    user1.email = String::from("anotheremail@example.com");

    // 使用结构体更新语法从其他实例创建实例
    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };

}

// 创建构造函数
// 参数名与字段名都完全相同，我们可以使用字段初始化简写语法
fn build_user(email: String, username: String) -> User {
    // 变量与字段同名时的字段初始化简写语法
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}


/// 使用没有命名字段的元组结构体来创建不同的类型
/// 元组结构体有着结构体名称提供的含义，但没有具体的字段名，只有字段的类型。
/// 当你想给整个元组取一个名字，并使元组成为与其他元组不同的类型时，元组结构体是很有用的，
/// 这时像常规结构体那样为每个字段命名就显得多余和形式化了。
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
/// 注意 black 和 origin 值的类型不同，因为它们是不同的元组结构体的实例。
/// 你定义的每一个结构体有其自己的类型，即使结构体中的字段有着相同的类型

fn structure_2() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}


/// 没有任何字段的类单元结构体
struct AlwaysEqual;

fn structure_3() {
    let subject = AlwaysEqual;
}


/// 结构体数据的所有权
/// 在上面 User 结构体的定义中，我们使用了自身拥有所有权的 String 类型而不是 &str 字符串 slice 类型。
/// 这是一个有意而为之的选择，因为我们想要这个结构体拥有它所有的数据，为此只要整个结构体是有效的话其数据也是有效的。
/// 可以使结构体存储被其他对象拥有的数据的引用，不过这么做的话需要用上生命周期（lifetime），
/// 生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的


// 结构体的打印输出
// {} 默认告诉 println! 使用被称为 Display 的格式：意在提供给直接终端用户查看的输出。
// 目前为止见过的基本类型都默认实现了 Display，因为它就是向用户展示 1 或其他任何基本类型的唯一方式。
// 不过对于结构体，println! 应该用来输出的格式是不明确的，因为这有更多显示的可能性：是否需要逗号？需要打印出大括号吗？
// 所有字段都应该显示吗？由于这种不确定性，Rust 不会尝试猜测我们的意图，所以结构体并没有提供一个 Display 实现。
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn structure_4() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    // 在 {} 中加入 :? 指示符告诉 println! 我们想要使用叫做 Debug 的输出格式。Debug 是一个 trait，
    // 它允许我们以一种对开发者有帮助的方式打印结构体，以便当我们调试代码时能看到它的值。
    // 我们必须为结构体显式选择这个功能。为此，在结构体定义之前加上外部属性 #[derive(Debug)]
    // 当我们有一个更大的结构体时，能有更易读一点的输出就好了，为此可以使用 {:#?} 替换 println! 字符串中的 {:?}
    println!("rect1 is {:?}", rect1);
    // {:#?} 格式化输出打印
    println!("rect1 is {:#?}", rect1);


    // 另一种使用 Debug 格式打印数值的方法是使用 dbg! 宏。
    // dbg! 宏接收一个表达式的所有权，打印出代码中调用 dbg! 宏时所在的文件和行号，
    // 以及该表达式的结果值，并返回该值的所有权。
    dbg!(rect1);
    dbg!(&rect1);
}
